#include <QtGui>
#include <cmath>
#include <QPalette>
#include "plotter.h"
#include <iostream>
#include "dbutil.h"
using namespace std;

Plotter::Plotter(QWidget *parent)
    : QWidget(parent)
{
    //    QPalette palette;
    //    palette.setBrush(QPalette::Background,QBrush(Qt::red));
    //    setPalette(palette);
    //    setBackgroundRole(QPalette::Dark);
    //    setAutoFillBackground(true);
    //    QPixmap pix;
    //    pix.load("images/bac.png");
    //    QPalette palette1;
    //    palette1.setBrush(QPalette::Background,QBrush(QPixmap(pix)));
    //    palette1.setBrush(QPalette::Window,QBrush(QPixmap(pix).scaled(
    //    size(),Qt::IgnoreAspectRatio,Qt::SmoothTransformation)));
    //    setPalette(palette1);
    //    setAutoFillBackground(true);
    this->wid = this->parentWidget()->width();
    this->hei = this->parentWidget()->height();
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    setFocusPolicy(Qt::StrongFocus);
    rubberBandIsShown = false;

    zoomInButton = new QToolButton(this);
    zoomInButton->setIcon(QIcon(":/images/zoomin.png"));
    zoomInButton->adjustSize();
    connect(zoomInButton, SIGNAL(clicked()), this, SLOT(zoomIn()));
    zoomInButton->hide();

    zoomOutButton = new QToolButton(this);
    zoomOutButton->setIcon(QIcon(":/images/zoomout.png"));
    zoomOutButton->adjustSize();
    connect(zoomOutButton, SIGNAL(clicked()), this, SLOT(zoomOut()));
    zoomOutButton->hide();
    //    QPen penl;
    //    penl.setColor(Qt::black);
    //setPlotSettings(PlotSettings());
}

void Plotter::setPlotSettings(const PlotSettings &settings)
{
    zoomStack.clear();
    zoomStack.append(settings);
    curZoom = 0;
    zoomInButton->hide();
    zoomOutButton->hide();
    refreshPixmap();
}

void Plotter::zoomOut()
{
    if (curZoom > 0)
    {
        --curZoom;
        zoomOutButton->setEnabled(curZoom > 0);
        zoomInButton->setEnabled(true);
        zoomInButton->hide();
        //refreshPixmap();
    }
}

void Plotter::zoomIn()
{
    if (curZoom < zoomStack.count() - 1)
    {
        ++curZoom;
        zoomInButton->setEnabled(curZoom < zoomStack.count() - 1);
        zoomOutButton->setEnabled(true);
        zoomOutButton->hide();
        //refreshPixmap();
    }
}

void Plotter::setCurveData(int id, const QVector<QPointF> &data)
{
    curveMap[id] = data;
    //refreshPixmap();
}

void Plotter::clearCurve(int id)
{
    if (curveMap.contains(id))
    {
        curveMap.remove(id);
        refreshPixmap();
    }
}

QMap<int, QVector<QPointF> > Plotter::getCurvMap()
{
    return this->curveMap;
}

QSize Plotter::minimumSizeHint() const
{
    return QSize(6 * Margin, 4 * Margin);
}

QSize Plotter::sizeHint() const
{
    return QSize(12 * Margin, 8 * Margin);
}

void Plotter::paintEvent(QPaintEvent * /* event */)
{
    QStylePainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.drawPixmap(0, 0, pixmap);
    if (rubberBandIsShown)
    {
        painter.setPen(palette().light().color());
        painter.drawRect(rubberBandRect.normalized()
                         .adjusted(0, 0, -1, -1));
    }

    if (hasFocus())
    {
        QStyleOptionFocusRect option;
        option.initFrom(this);
        option.backgroundColor = palette().dark().color();
        painter.drawPrimitive(QStyle::PE_FrameFocusRect, option);
    }
}

void Plotter::resizeEvent(QResizeEvent * /* event */)
{
    int x = width() - (zoomInButton->width()
                       + zoomOutButton->width() + 10);
    zoomInButton->move(x, 5);
    zoomOutButton->move(x + zoomInButton->width() + 5, 5);
    //refreshPixmap();
}

void Plotter::mousePressEvent(QMouseEvent *event)
{
    //    QRect rect(Margin, Margin,
    //               width() - 2 * Margin, height() - 2 * Margin);
    //    if (event->button() == Qt::LeftButton) {
    //        if (rect.contains(event->pos())) {
    //            rubberBandIsShown = true;
    //            rubberBandRect.setTopLeft(event->pos());
    //            rubberBandRect.setBottomRight(event->pos());
    //            updateRubberBandRegion();
    //            setCursor(Qt::CrossCursor);
    //        }
    //    }
}

void Plotter::mouseMoveEvent(QMouseEvent *event)
{
    if (rubberBandIsShown)
    {
        updateRubberBandRegion();
        rubberBandRect.setBottomRight(event->pos());
        updateRubberBandRegion();
    }
}

void Plotter::mouseReleaseEvent(QMouseEvent *event)
{
    if ((event->button() == Qt::LeftButton) && rubberBandIsShown)
    {
        rubberBandIsShown = false;
        updateRubberBandRegion();
        unsetCursor();

        QRect rect = rubberBandRect.normalized();
        if (rect.width() < 4 || rect.height() < 4)
        {
            return;
        }
        rect.translate(-Margin, -Margin);

        PlotSettings prevSettings = zoomStack[curZoom];
        PlotSettings settings;
        double dx = prevSettings.spanX() / (width() - 2 * Margin);
        double dy = prevSettings.spanY() / (height() - 2 * Margin);
        settings.minX = prevSettings.minX + dx * rect.left();
        settings.maxX = prevSettings.minX + dx * rect.right();
        settings.minY = prevSettings.maxY - dy * rect.bottom();
        settings.maxY = prevSettings.maxY - dy * rect.top();
        settings.adjust();

        zoomStack.resize(curZoom + 1);
        zoomStack.append(settings);
        zoomIn();
    }
}

void Plotter::keyPressEvent(QKeyEvent *event)
{
    switch (event->key())
    {
    case Qt::Key_Plus:
        zoomIn();
        break;
    case Qt::Key_Minus:
        zoomOut();
        break;
    case Qt::Key_Left:
        zoomStack[curZoom].scroll(-1, 0);
        //refreshPixmap();
        break;
    case Qt::Key_Right:
        zoomStack[curZoom].scroll(+1, 0);
        //refreshPixmap();
        break;
    case Qt::Key_Down:
        zoomStack[curZoom].scroll(0, -1);
        //refreshPixmap();
        break;
    case Qt::Key_Up:
        zoomStack[curZoom].scroll(0, +1);
        //refreshPixmap();
        break;
    default:
        QWidget::keyPressEvent(event);
    }
}

void Plotter::wheelEvent(QWheelEvent *event)
{
    int numDegrees = event->delta() / 8;
    int numTicks = numDegrees / 15;

    if (event->orientation() == Qt::Horizontal)
    {
        zoomStack[curZoom].scroll(numTicks, 0);
    }
    else
    {
        zoomStack[curZoom].scroll(0, numTicks);
    }
    //refreshPixmap();
}

void Plotter::updateRubberBandRegion()
{
    QRect rect = rubberBandRect.normalized();
    update(rect.left(), rect.top(), rect.width(), 1);
    update(rect.left(), rect.top(), 1, rect.height());
    update(rect.left(), rect.bottom(), rect.width(), 1);
    update(rect.right(), rect.top(), 1, rect.height());
}

void Plotter::refreshPixmap()
{
    pixmap = QPixmap(this->wid, this->hei);
    pixmap.fill(this, 0, 0);
    QPainter painter(&pixmap);
    painter.initFrom(this);
    drawGrid(&painter);
    drawCurves(&painter);
    update();
}
void Plotter::setWidth(int wid)
{
    this->wid = wid;
}
void Plotter::setHeigth(int hei)
{
    this->hei = hei;
}
int Plotter::width()
{
    return  this->wid;
}
int Plotter::height()
{
    return this->hei;
}

void Plotter::drawGrid(QPainter *painter)
{
    PlotSettings settings = zoomStack[curZoom];
    QRect rect(settings.Marginleft, settings.Marginup,
               width() - (settings.Marginleft + settings.Marginright), height() - (settings.Marginup + settings.Margindown));
    if (!rect.isValid())
    {
        return;
    }
    // PlotSettings settings = zoomStack[curZoom];
    QPen quiteDark = palette().dark().color().light();
    //    QPen quiteDark = QPen(QColor(175,175,175), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
    QPen light = QPen(QColor(Qt::lightGray), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
    //    QPen light = QPen(QColor(175,175,175), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
    painter->setRenderHint(QPainter::Antialiasing);
    painter->setPen(quiteDark);
    painter->drawLine(rect.left(), rect.top(), rect.left(), rect.bottom());
    for (int i = 0; i <= settings.numXTicks; ++i)
    {
        int x = rect.left() + (i * (rect.width() - 1)
                               / settings.numXTicks);
        //        double label = settings.minX + (i * settings.spanX()
        //                                          / settings.numXTicks);
        if (i == 0)
        {
            quiteDark = QPen(QColor(Qt::darkGray), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
        }
        else
        {
            quiteDark = QPen(QColor(Qt::lightGray), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
        }
        painter->setPen(quiteDark);
        painter->drawLine(x, rect.top(), x, rect.bottom());
        painter->setPen(light);
        painter->drawLine(x, rect.bottom(), x, rect.bottom() + 5);
        QPen penxx1;
        penxx1.setColor(Qt::black);
        painter->setPen(penxx1);
        if (settings.numXTicks <= 9000)
        {
            painter->drawText(x - 50 , rect.bottom() + 5, 100, 20,
                              Qt::AlignHCenter | Qt::AlignTop,
                              QString(settings.str[i]));
        }
        else
        {
            painter->drawText(x - 50 , rect.bottom() + 5, 100, 20,
                              Qt::AlignHCenter | Qt::AlignTop,
                              QString(""));
            if (i == settings.numXTicks - 10 || i == (settings.numXTicks - 10) / 2)
            {
                painter->drawText(x - 50 , rect.bottom() + 5, 100, 20,
                                  Qt::AlignHCenter | Qt::AlignTop,
                                  QString(DbUtils::getStringFormInt(i)));
            }
        }
    }
    quiteDark = palette().dark().color().light();
    for (int j = 0; j <= settings.numYTicks; ++j)
    {
        int y = rect.bottom() - (j * (rect.height() - 1)
                                 / settings.numYTicks);
        double label = settings.minY + (j * settings.spanY()
                                        / settings.numYTicks);
        if (j == 0)
        {
            quiteDark = QPen(QColor(Qt::darkGray), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
        }
        else
        {
            quiteDark = QPen(QColor(Qt::lightGray), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
        }
        painter->setPen(quiteDark);
        painter->drawLine(rect.left(), y, rect.right(), y);//X axis
        painter->setPen(light);
        painter->drawLine(rect.left() - 5, y, rect.left(), y);
        QPen penxx;
        penxx.setColor(Qt::black);
        painter->setPen(penxx);
        //        char ch[16];
        //        sprintf(ch,"%.*f",settings.scale,label);
        //        QString label1(ch);
        painter->drawText(rect.left() - settings.Marginleft, y - 10, settings.Marginleft - 5, 20,
                          Qt::AlignRight | Qt::AlignVCenter,
                          QString::number(label));
    }
    QPen penxx2;
    penxx2.setColor(Qt::transparent);
    painter->setPen(penxx2);
    painter->drawRect(rect.adjusted(0, 0, -1, -1));
}

void Plotter::drawCurves(QPainter *painter)
{
    painter->setRenderHint(QPainter::Antialiasing);
    static const QColor colorForIds[6] =
    {
        Qt::blue, Qt::red, Qt::green, Qt::cyan, Qt::magenta, Qt::yellow
    };
    PlotSettings settings = zoomStack[curZoom];
    QRect rect(settings.Marginleft, settings.Marginup,
               width() - (settings.Marginleft + settings.Marginright), height() - (settings.Marginup + settings.Margindown));
    if (!rect.isValid())
    {
        return;
    }

    painter->setClipRect(rect.adjusted(+1, +1, -1, -1));

    QMapIterator<int, QVector<QPointF> > i(curveMap);
    while (i.hasNext())
    {
        i.next();

        int id = i.key();
        QVector<QPointF> data = i.value();
        QPolygonF polyline(data.count());

        for (int j = 0; j < data.count(); ++j)
        {
            double dx = data[j].x() - settings.minX;
            double dy = data[j].y() - settings.minY;
            double x = rect.left() + (dx * (rect.width() - 1)
                                      / settings.spanX());
            double y = rect.bottom() - (dy * (rect.height() - 1)
                                        / settings.spanY());
            polyline[j] = QPointF(x, y);
        }
        //        painter->setPen(settings.pen);
        QPen qp = settings.pens.value(DbUtils::getStringFormInt(id));
        painter->setPen(qp);
        painter->drawPolyline(polyline);
    }
}

PlotSettings::PlotSettings()
{
    minX = 0.0;
    maxX = 10.0;
    numXTicks = 5;

    minY = 0.0;
    maxY = 10.0;
    numYTicks = 5;

    scale = 6;
}

void PlotSettings::scroll(int dx, int dy)
{
    double stepX = spanX() / numXTicks;
    minX += dx * stepX;
    maxX += dx * stepX;

    double stepY = spanY() / numYTicks;
    minY += dy * stepY;
    maxY += dy * stepY;
}

void PlotSettings::adjust()
{
    adjustAxis(minX, maxX, numXTicks);
    adjustAxis(minY, maxY, numYTicks);
}

void PlotSettings::adjustAxis(double &min, double &max, int &numTicks)
{
    const int MinTicks = 4;
    double grossStep = (max - min) / MinTicks;
    double step = std::pow(10.0, std::floor(std::log10(grossStep)));

    if (5 * step < grossStep)
    {
        step *= 5;
    }
    else if (2 * step < grossStep)
    {
        step *= 2;
    }

    numTicks = int(std::ceil(max / step) - std::floor(min / step));
    if (numTicks < MinTicks)
    {
        numTicks = MinTicks;
    }
    min = std::floor(min / step) * step;
    max = std::ceil(max / step) * step;
}
